/**
 *  @file
 *  @brief   坐标系基类定义
 *  @author  wanghuan
 *  @date    2024.04.16
 *  @version  v1.0
 *  @par Copyright(c):  ShenGong  Simulation corporation
 */

#pragma once
#include <math.h>

#include <map>
#include <memory>

#include "Utility/Algebra/Point.h"
#include "Utility/Algebra/Vector3D.h"

#include "DataStructure/Common/Constant.h"
#include "DataStructure/Common/DataStructureExport.h"
#include "DataStructure/Common/DataStructureNamespace.h"
#include "DataStructure/Common/Id.h"

#include "CoordTypes.h"

SG_DATASTRUCTURE_FEM_NAMESPACE_OPEN
/** 坐标系的状态*/
enum CoordStatus
{
    Status_Initialized = 0,  ///< 坐标系的原点和基矢量都计算完毕
    Status_Ready       = 1,  ///< 坐标系依赖的其他下层坐标系都初始化了。
    Status_NeedOther   = 2,  ///< 依赖的坐标系还需处理
};

struct ICoordinate;
using ICoordinateP      = std::shared_ptr<ICoordinate>;
using ICoordinatePArray = std::map<SG::DataStructure::Common::Id, ICoordinateP>;

/** @brief 坐标系基类定义
 *
 */
struct DECLSPEC ICoordinate
{
    ICoordinate () : ICoordinate (SG::DataStructure::Common::DEFAULT_ID, CoordTypes::COORDTYPE_UnKonwn)
    {}

    ICoordinate (SG::DataStructure::Common::Id id, SG::DataStructure::FEM::CoordTypes type)
        : m_id (id), m_type (type), m_status (Status_NeedOther)
    {}

    virtual ~ICoordinate () = default;

    /** @brief 传入单元参考坐标系数据
     * 此函数只有 第一型（COORDTYPE_Cartesian, COORDTYPE_Cylinder, COORDTYPE_Sphere 可以传入三个值
     * 其他类型坐标系能传入一个值（这些值在构造时传入， 因此此函数可以不调用）。
     */
    virtual void SetRefCoordId (std::size_t index, SG::DataStructure::Common::Id refId) = 0;

    /** @brief 查询坐标系状态 */
    virtual SG::DataStructure::FEM::CoordStatus QueryStatus (ICoordinatePArray& coordSet) const = 0;

    /** @brief 获得定义坐标系的参考点Id， 仅仅能被第一型坐标系调用*/
    virtual void GetRefPoint (_OUT SG::DataStructure::Common::Id& GA,
                              _OUT SG::DataStructure::Common::Id& GB,
                              _OUT SG::DataStructure::Common::Id& GC) const = 0;

    /** @brief 获得定义坐标系的参考坐标系和参考点， 仅仅能被第二型坐标系调用*/
    virtual void GetRefPoint (_OUT SG::DataStructure::Common::Id& ref,
                              _OUT SG::Algebra::Point& PA,
                              _OUT SG::Algebra::Point& PB,
                              _OUT SG::Algebra::Point& PC) = 0;

    /** @brief 计算坐标系的原点和 基矢量
     * 当 m_status == CoordStatus 时， 才能够调用本函数
     * 执行完毕后， 程序变量 m_status 赋值为 Status_Initialized
     * @param [in] GA, GB, GC 三个全局坐标下的点
     */
    void Compute (const SG::Algebra::Point& GA, const SG::Algebra::Point& GB, const SG::Algebra::Point& GC);

    /** @brief 计算节点在世界坐标系下分量
     *
     * 调用时， 成员变量 m_status 需要等于 Status_Initialized
     * 对于柱坐标系， x 即 r， y 即 /theta
     * 对于球坐标系， x 即 r， y 即 /theta, z 即 /phi
     * 对于柱坐标系、球坐标系中的角度分量，单位均为度
     */
    virtual SG::Algebra::Point Trans2Basic (const SG::DataStructure::Common::Real& x,
                                            const SG::DataStructure::Common::Real& y,
                                            const SG::DataStructure::Common::Real& z) const = 0;

    SG::DataStructure::FEM::CoordTypes GetType () const;

    // 获取坐标系原点
    const SG::Algebra::Point& GetOrigin () const;
    // 获取坐标系基矢量
    const SG::Algebra::Vector3D& GetBaseX () const;
    const SG::Algebra::Vector3D& GetBaseY () const;
    const SG::Algebra::Vector3D& GetBaseZ () const;

    SG::DataStructure::Common::Id      m_id;    ///< 坐标系编号
    SG::DataStructure::FEM::CoordTypes m_type;  ///< 坐标系类型
  protected:
    SG::Algebra::Point    m_origin;             ///< 坐标系原点
    SG::Algebra::Vector3D m_bases[3];           ///< 坐标系的三个基矢量
    CoordStatus           m_status;             ///< 坐标系状态
};

// 坐标基计算
inline void ICoordinate::Compute (const SG::Algebra::Point& GA, const SG::Algebra::Point& GB, const SG::Algebra::Point& GC)
{
    SG::Algebra::Vector3D Z = Distance (GB, GA);
    SG::Algebra::Vector3D X = Distance (GC, GA);  // 临时储存G13
    SG::Algebra::Vector3D Y = Cross (Z, X);
    X                       = Cross (Y, Z);

    m_origin = GA;

    Common::Real tmp;
    m_bases[0] = X.Normalize (tmp);
    m_bases[1] = Y.Normalize (tmp);
    m_bases[2] = Z.Normalize (tmp);

    // 完成计算后，修改状态
    m_status = Status_Initialized;
}

/** 获取坐标系类型 */
inline SG::DataStructure::FEM::CoordTypes ICoordinate::GetType () const
{
    return m_type;
}

// 获取坐标系原点
inline const SG::Algebra::Point& ICoordinate::GetOrigin () const
{
    return m_origin;
}
// 获取坐标系基矢量
inline const SG::Algebra::Vector3D& ICoordinate::GetBaseX () const
{
    return m_bases[0];
}

inline const SG::Algebra::Vector3D& ICoordinate::GetBaseY () const
{
    return m_bases[1];
}

inline const SG::Algebra::Vector3D& ICoordinate::GetBaseZ () const
{
    return m_bases[2];
}

SG_DATASTRUCTURE_FEM_NAMESPACE_CLOSE